unit lyric;
interface
uses Classes,SysUtils;
type
TOneLyric=Record
time:longint;
lyStr:string;
end;
TLyric=class
private
FFilename:string;
FOffset:integer; //时间补偿值 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。
FAur:string; //艺人名
FBy:string; //编者(指编辑LRC歌词的人)
FAl:string; //专辑名
FTi:string; //曲名
FCount:integer;
FLyricArray : array of TOneLyric;
function GetLyric(i:integer): TOneLyric ;
function ExistTime(vTime:longint):boolean;
procedure sortLyric;
procedure ResetLyrics(FTxt:Tstrings);
protected
public
constructor Create;
destructor Destroy; override;
procedure loadLyric(afilename:string);
procedure SetTxt(aLyrics:Tstrings);
procedure UnloadLyric(strs:Tstrings);
//整体提前(正值)或整体延后(负值)atime毫秒
function ChgOffset(atime:integer):boolean;
//提前(正值)/延后(负值)某一句歌词 aTime毫秒
function ChgOneLyric(oldTime:longint;aTime:integer):boolean;
//保存歌词内容到文件
function SaveLyricsToFile(vFileName:string):boolean;
property filename:string read ffilename;
property Ar:string read FAur;
property By:string read FBy;
property Al:string read FAl;
property Ti:string read FTi;
property Offset :integer read FOffset;
property LyricArray[i:integer]: TOneLyric read GetLyric ;
property Count:integer read FCount;
end;
implementation
constructor TLyric.Create;
begin
inherited Create;
FTi:='';
FAur:='';
Fal:='';
FBy:='';
FOffset:=0;
end;
function TLyric.ExistTime(vTime:longint):boolean;
var i:integer;
begin
result:=false;
for i:=0 to length(FLyricArray) -1 do
if FLyricArray[i].time =vTime then
begin
result:=true;
break;
end;
end;
procedure TLyric.loadLyric(afilename:string);
var
FTxt:Tstrings;
begin
FTi:='';
FAur:='';
Fal:='';
FBy:='';
FOffset:=0;
FFilename:=afilename;
//载入歌词
FTxt:=TStringlist.create;
FTxt.LoadFromFile(Ffilename);
ResetLyrics(FTxt);
FTxt.Clear;
FTxt.free;
end;
procedure TLyric.SetTxt(aLyrics:Tstrings);
begin
FTi:='';
FAur:='';
Fal:='';
FBy:='';
FOffset:=0;
//载入歌词
ResetLyrics(aLyrics);
end;
//根据歌词文件 载入每行歌词
procedure TLyric.ResetLyrics(FTxt:Tstrings);
var i:integer;
function makeOneLyric(CurLyric:string):string;
var p1,p2,p3:integer;
timestr,lyricstr:string;
time1,time2:longint;
isFuSign:boolean;
begin
p1:=pos('[',CurLyric);//第一个‘[’位置
if p1=0 then begin //判断是否非法行
result:= CurLyric; //无 '['
exit;
end;
p2:=pos(']',CurLyric); //第一个‘]’位置
if p2=0 then begin
result:= CurLyric;
exit; //无 ']'
end;
timestr:=copy(curLyric,p1+1,p2-p1-1);
//左右两边为歌词
lyricStr:= copy(curLyric,1,p1-1) + copy(curLyric,p2+1,length(curLyric)-p2) ;
lyricStr:=makeOneLyric(lyricStr);
if copy(Lowercase(timeStr),1,2)='ar' then //作者信息
FAur:= copy(timestr,4,length(timestr)-3)
else if copy(Lowercase(timeStr),1,2)='ti' then //曲目标题
FTi:= copy(timestr,4,length(timestr)-3)
else if copy(Lowercase(timeStr),1,2)='al' then //专辑名
FAl:= copy(timestr,4,length(timestr)-3)
else if copy(Lowercase(timeStr),1,2)='by' then //编辑LRC歌词的人
FBy:= copy(timestr,4,length(timestr)-3)
else if copy(Lowercase(timeStr),1,6)='offset' then //时间补偿值
try
FOffset:= strtoint(copy(timestr,8,length(timestr)-7))
except
FOffset:=0;
end
else //此时为 时间标记
begin
p3:= pos(':',timestr) ;
if p3>0 then begin //判断是否非法行
isFuSign:=false;
try
time1:=strtoint(copy(timestr,1,p3-1))*1000;
if time1<0 then
isFuSign:=true; //记录 该 时间标签 为 负(小于零)
except
//非法歌词行
exit; //分钟有误
end;
try
time2:= trunc( strtofloat(copy(timestr,p3+1,length(timestr)-p3)) *1000);
if isFuSign then
time2:=-time2;
except
exit; //秒 有误
end;
if not ExistTime(time1*60+time2) then
begin
setLength(FLyricArray,length(FLyricArray)+1);
//if trim(lyricStr)=' then
// lyricStr:= '(Music)';
with FLyricArray[length(FLyricArray)-1] do
begin
time :=time1*60+time2;
lystr:=lyricStr;
end;
result:=lyricStr;
end;
end;
end;
end;
begin
SetLength(FLyricArray,0);
//解析歌词各部分
for i:= 0 to FTxt.count-1 do
begin
makeOneLyric(FTxt[i]) ;
end;
FCount:=length(FLyricArray) ;
sortLyric;
{if FTi<>' then FTi:= replaceWithchr(FTi,'&','&&');
if FAur<>' then FAur:= replaceWithchr(FAur,'&','&&');
if FAl<>' then FAl:= replaceWithchr(FAl,'&','&&');
if FBy<>' then FBy:= replaceWithchr(FBy,'&','&&');
}
//根据 整体时间偏移,重新计算每句歌词时间
for i:=0 to length(FLyricArray)-1 do
begin
//FLyricArray[i].lyStr := replaceWithchr(FLyricArray[i].lyStr,'&','&&');
if FLyricArray[i].time >= 0 then
begin
if FLyricArray[i].time - FOffset>=0 then
FLyricArray[i].time:=FLyricArray[i].time - FOffset ;
end
else
FLyricArray[i].time:=0;
end;
end;
procedure TLyric.UnloadLyric(strs:Tstrings);
var i:integer;
begin
SetLength(FLyricArray,strs.Count );
FCount:= strs.Count;
for i:=0 to strs.Count -1 do
begin
FLyricArray[i].time :=0;
FLyricArray[i].lyStr := strs[i];
end;
FTi:='';
FAur:='';
Fal:='';
FBy:='';
FOffset:=0;
end;
destructor TLyric.Destroy;
begin
SetLength(FLyricArray,0);
inherited Destroy;
end;
function TLyric.GetLyric(i:integer): TOneLyric ;
begin
if (i>=0) and (i<length(FLyricArray)) then
result:=FLyricArray[i] ;
end;
procedure TLyric.sortLyric;
var i,j:integer;
tmpLyric:TOneLyric;
begin
for i:=0 to length(FLyricArray)-2 do
begin
for j:=i to length(FLyricArray)-1 do
begin
if FLyricArray[j].time < FLyricArray[i].time then
begin
tmpLyric:= FLyricArray[i];
FLyricArray[i]:= FLyricArray[j];
FLyricArray[j]:= tmpLyric;
end;
end;
end;
end;
function TLyric.ChgOffset(atime:integer):boolean;//提前(正值)或延后(负值)atime毫秒
var i,numberLine:integer;
p1,p2,p3:integer;
timestr,lyricstr,CurLyric,signStr:string;
aOffset:longint;
afind:boolean;
FTxt:Tstrings;
begin
Result:=false;
//修改offset 保存文件
afind:=false;
aOffset:=0;
numberLine:=-1;
FTxt:=TStringlist.create;
try
FTxt.LoadFromFile(FFilename);
for i:=0 to FTxt.Count-1 do
begin
curLyric:=fTxt[i];
p1:=pos('[',CurLyric);//第一个‘[’位置
if p1=0 then begin //判断是否非法行
continue; //无 '['
end;
p2:=pos(']',CurLyric); //第一个‘]’位置
if p2=0 then begin
continue; //无 ']'
end;
timestr:=copy(curLyric,p1+1,p2-p1-1);
//左右两边为歌词
lyricStr:= copy(curLyric,1,p1-1) + copy(curLyric,p2+1,length(curLyric)-p2) ;
if copy(Lowercase(timeStr),1,6)='offset' then //找到 时间补偿串
begin
try
aOffset:= strtoint(copy(timestr,8,length(timestr)-7));
except
continue;
end ;
fTxt[i]:=copy(curLyric,1,p1-1)
+'[offset:'+inttostr(aOffset+aTime) +']'
+copy(curLyric,p2+1,length(curLyric)-p2);
if aOffset+aTime=0 then
FTxt.Delete(i);
fTxt.SaveToFile(FFilename);
afind:=true;
break;
end
else
begin
if numberLine=-1 then
begin
signStr:=copy(Lowercase(timeStr),1,2) ;
if (signStr<>'ar') and (signStr<>'al') and (signStr<>'ti') and (signStr<>'by') then begin
p3:= pos(':',timestr) ;
if p3>0 then //为时间标记
numberLine:=i; //记录行号
end;
end;
end;
end;
if (not afind) and (numberLine<>-1) then
begin
//fTxt.Add('[offset:'+inttostr(aOffset+aTime) +']');
fTxt.Insert(numberline, '[offset:'+inttostr(aOffset+aTime) +']');
fTxt.SaveToFile(FFilename);
end;
//重新载入歌词
loadLyric(FFilename);
result:=true;
FTxt.Clear;
finally
FTxt.free;
end;
end;
function TLyric.ChgOneLyric(oldTime:longint;aTime:integer):boolean;
var i:integer;
FTxt:Tstrings;
AjustOk:boolean;
thisLyric:string;
function AjustOneLine(var curLyric:string):boolean;
var
isFu:boolean;//该时间是否为负.
p1,p2,p3:integer;
timestr,left_lyric,right_lyric:string; //
time1,time2:longint;
findok:boolean;
isValid:boolean;//
NewTimeLabel:string;
NewTime:longint;
UseMS:boolean;
begin
//---------该串内 是否有标签 ----------
p1:=pos('[',CurLyric);//第一个‘[’位置
if p1=0 then begin //判断是否非法行
Result:=false; //无 '['
exit;
end;
p2:=pos(']',CurLyric); //第一个‘]’位置
if p2=0 then begin
result:= false;
exit; //无 ']'
end;
//==========串内 是否有标签==========
//----------目前标签 是否是 指定时间的 标签----------
timestr:=copy(curLyric,p1+1,p2-p1-1);
Left_lyric:= copy(curLyric,1,p1-1);
Right_lyric:= copy(curLyric,p2+1,length(curLyric)-p2) ;
isValid:=true;
findok:=false;
p3:= pos(':',timestr) ;
if p3>0 then begin //判断是否 合法时间标签
isFu:=false;
try
time1:=strtoint(copy(timestr,1,p3-1))*1000;
if time1<0 then
isFu:=true;
except
//非法歌词行
isValid:=false; //分钟有误
end;
try
if isValid then
begin
time2:= trunc( strtofloat(copy(timestr,p3+1,length(timestr)-p3)) *1000);
if isFu then
time2:=-time2;
end;
except
isValid:=false; //秒 有误
end;
if isValid and (time1*60+time2 - FOffset = oldtime) then //找到 指定时间串
findOk:=true; //找到 啦 啦 啦 ...
end; //非法行 判断结束
//==========是否找到 指定时间标签==========
//-------找到 和 没找到 之后的处理 ----------
if findok then begin
Result:= true;
//根据 老时间标签(timerstr)
//生成 新的时间标签
NewTime:=time1*60+time2 - aTime;
//根据 原来是否使用毫秒 和 目前调整的偏移时间是否属于毫秒级别
// 来决定 是否 使用毫秒
UseMS:= (Pos('.',timestr)>0) or (aTime mod 1000 <>0);
//转化时间为时间串例如: 72秒 ==>> '00:01:12.000'
NewTimeLabel:=ConvertTimeToTimestr(NewTime,0,false,true,UseMS,true);
//返回 新的歌词部分
curLyric := left_Lyric + '[' + NewTimeLabel + ']'+ Right_Lyric;
end
else //还 没找到
begin //在 该行歌词剩余部分 找找看
if AjustOneLine(Right_lyric) then
begin
//在剩余部分内找到了
curLyric:= Left_lyric+ '[' + timestr +']' +Right_lyric ;
Result:=true;
end
//else 剩余部分内 没有的话 ,只好返回 false 啦
// (函数开始处,已经默认=false)
end;
//=========找到 和 没找到 之后的处理==========
end; //end function AjustOneLine
begin
AjustOk:=false;
Result:=false;
FTxt:=TStringlist.create;
try
FTxt.LoadFromFile(FFilename);
for i:=0 to FTxt.Count-1 do
begin
thisLyric:=FTxt[i];
if AjustOneLine(thisLyric) then
begin
AjustOk:=true;
//更新 新生成 的歌词行
FTxt[i]:=thisLyric;
break;
end;
end;
if AjustOk then
begin
//保存歌词文件
FTxt.SaveToFile(FFilename);
//重新载入歌词
loadLyric(FFilename);
Result:=true;
end;
FTxt.Clear;
finally
FTxt.free;
end;
end;
//保存歌词内容到文件
function TLyric.SaveLyricsToFile(vFileName:string):boolean;
var i:integer;
aTxt:Tstrings ;
begin
result:=false;
if FCount<=0 then exit;
aTxt:=Tstringlist.Create;
try
if FTi <>' then
aTxt.Add('歌名:'+FTi);
if FAur <>' then
aTxt.Add('歌手:'+FAur);
if Fal <>' then
aTxt.Add('专辑:'+Fal);
// if Fby <>' then
// lbxpreview.Items.Add('歌词编辑:'+Fby);
if aTxt.Count >0 then
aTxt.Add('--- --- --- --- --- --- ---');
for i:=0 to FCount-1 do
aTxt.Add(LyricArray[i].lyStr ) ;
aTxt.SaveToFile(vFilename);
Result:=true;
finally
aTxt.Free;
end;
end;
end.